#include "oj_compile_run/oj_compiler.h"
namespace ojlb {
namespace oj_compiler_run {


bool OjCompiler::compile(const oj_common::OjCodeFile& oj_code_file) { // TODO: 这个方法的实现有些长了, 可以考虑做子函数
    // 检查编译环境
    checkComplieEnv(oj_code_file);

    // 清理编译环境
    cleanCompileEnv(oj_code_file);

    // 创建子进程虚拟编译器
    pid_t fork_result = fork();
    
    // 使得标准流重定向
    oj_common::OjStreaEdirection ojse = oj_common::OjStreaEdirection(oj_code_file.getErrorPath().c_str()); // 无需释放, 反正子进程会在执行完后被回收
    ojse.setNewStdErrFilePid(); // 只需要重定向标准错误

    // 使用子进程虚拟编译器
    if (fork_result < 0) { // 异常
        handleForkError();
    } else if (fork_result == 0) { // 子进程
        handleChildProcess(oj_code_file);
    } else { // 父进程
        return handleParentProcess(fork_result, oj_code_file, ojse); // TODO: 这里哪怕子进程异常了估计也无法察觉...以后改...
    }

    return false;
}

void OjCompiler::checkComplieEnv(const oj_common::OjCodeFile& oj_code_file){
    if (!oj_code_file.isExistOfSourcePath()) {
        oj_common::OjLog::error("用户的源代码文件不存在, 无法进行编译");
        exit(-1); // TODO: 抛出异常
    }
}

void OjCompiler::cleanCompileEnv(const oj_common::OjCodeFile& oj_code_file) {
    if (oj_code_file.isExistOfObjectPath()) {
        if (remove(oj_code_file.getObjectPath().c_str()) == 0) {
            oj_common::OjLog::debug("删除了之前编译遗留下来的目标文件");
        } else {
            oj_common::OjLog::warn("清理编译环境失败, 旧的目标文件无法删除");
            exit(-1); // TODO: 抛出异常
        }
    }
}

void OjCompiler::handleForkError() {
    oj_common::OjLog::error("虚拟编译器的子进程创建异常, 检查创建虚拟编译器的 fork() 是否出现问题");
    exit(-1); // TODO: 抛出异常
}

void OjCompiler::handleChildProcess(const oj_common::OjCodeFile& oj_code_file) {
    /** 运行结果情况分析
     * 1. 代码编译, 编译通过
     * 2. 代码编译, 编译不通过
     * 编译模块在编译通过时需要生成目标文件, 在编译不通过时需要生成错误文件
     */
    
    // 虚拟编译器编译语言
    std::string programming_language = oj_code_file.getProgrammingLanguage();

    if (programming_language == "c") {
        execlp("gcc", "gcc", "-o", oj_code_file.getObjectPath().c_str(), oj_code_file.getSourcePath().c_str(), nullptr);
    } else if (programming_language == "cpp") {
        execlp("g++", "g++", "-o", oj_code_file.getObjectPath().c_str(), oj_code_file.getSourcePath().c_str(), nullptr);
    } else if (programming_language == "java") {
        execlp("javac", "javac", oj_code_file.getSourcePath().c_str(), nullptr);
    } else if (programming_language == "python") {
        ; // execlp("python", "python", oj_code_file.getSourcePath().c_str(), nullptr); // TODO: 先不做 Python 的
        exit(-1); // TODO: 抛出异常, 暂不支持
    } else {
        exit(-1); // TODO: 抛出异常, 用户提供了不支持的编译语言, 等待未来项目更新
    } // TODO: 这里还没有把编译版本也算上...

    exit(-1); // TODO: 抛出异常, 虚拟编译器的子进程程序替换出现问题
}

bool OjCompiler::handleParentProcess(const pid_t& fork_result, const oj_common::OjCodeFile& oj_code_file, oj_common::OjStreaEdirection ojse) {
    waitpid(fork_result, nullptr, 0); // TODO: 暂时不对子进程的返回结果做关心, 阻塞等待就行
    
    ojse.resetNewStdErrFilePid(); // 恢复标准流重定向
    
    if (oj_code_file.isExistOfObjectPath()) { // 编译成功(由于编译器会自己生成目标文件, 因此看是否有目标文件的存在就可以判断是否编译成功)
        return true;
    } else { // 编译失败
        oj_common::OjLog::info("编译失败, 用户提交的代码中存在编译错误");
        return false;
    }
    // TODO: 这里对于 Python 的处理还有漏洞, 和 python 的特性有关
}


}
}
